using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace NFreeMarker.Cache
{
    /**
     * A {@link TemplateLoader} that uses a Map with Strings as its source of 
     * templates.
     *
     * In most case the regular way of loading templates from files will be fine.
     * However, there can be situations where you don't want to or can't load a
     * template from a file, e.g. if you have to deploy a single jar for 
     * JavaWebStart or if they are contained within a database.
     * A single template can be created manually
     * e.g.
     * <pre>
     *   string templateStr="Hello ${user}";
     *   Template t = new Template("name", new StringReader(templateStr),
     *               new Configuration());
     * </pre>
     * If, however, you want to create templates from strings which import other 
     * templates this method doesn't work.
     *
     * In that case you can create a StringTemplateLoader and add each template to 
     * it:
     * <pre>
     *   StringTemplateLoader stringLoader = new StringTemplateLoader();
     *   stringLoader.putTemplate("greetTemplate", "<#macro greet>Hello</#macro>");
     *   stringLoader.putTemplate("myTemplate", "<#include \"greetTemplate\"><@greet/> World!");
     * </pre>
     * Then you tell your Configuration object to use it:
     * <pre>
     *   cfg.setTemplateLoader(stringLoader);
     * </pre>
     * After that you should be able to use the templates as usual. Often you will
     * want to combine a <tt>StringTemplateLoader</tt> with another loader. You can
     * do so using a {@link freemarker.cache.MultiTemplateLoader}.
     *
     * @version $Id: v 1.0 2005/04/01
     * @author Meikel Bisping
     * @author Attila Szegedi
     * @version $Id: StringTemplateLoader.java,v 1.1 2005/04/08 11:47:53 szegedia Exp $
     */

    public class StringTemplateLoader : ITemplateLoader
    {
        private readonly Dictionary<string, StringTemplateSource> _templates =
            new Dictionary<string, StringTemplateSource>();

        /**
         * Puts a template into the loader. A call to this method is identical to 
         * the call to the three-arg {@link #putTemplate(string, string, long)} 
         * passing <tt>System.currentTimeMillis()</tt> as the third argument.
         * @param name the name of the template.
         * @param templateSource the source code of the template.
         */

        public void PutTemplate(string name, string templateSource)
        {
            // TODO: Is ToFileTime comparable to System.currentTimeMillis()?
            PutTemplate(name, templateSource, DateTime.Now.ToFileTime());
        }

        /**
         * Puts a template into the loader. The name can contain slashes to denote
         * logical directory structure, but must not start with a slash. If the 
         * method is called multiple times for the same name and with different
         * last modified time, the configuration's template cache will reload the 
         * template according to its own refresh settings (note that if the refresh 
         * is disabled in the template cache, the template will not be reloaded).
         * Also, since the cache uses lastModified to trigger reloads, calling the
         * method with different source and identical timestamp won't trigger
         * reloading.
         * @param name the name of the template.
         * @param templateSource the source code of the template.
         * @param lastModified the time of last modification of the template in 
         * terms of <tt>System.currentTimeMillis()</tt>
         */

        public void PutTemplate(string name, string templateSource, long lastModified)
        {
            _templates.Add(name, new StringTemplateSource(name, templateSource, lastModified));
        }

        public void CloseTemplateSource(object templateSource)
        {
        }

        public object FindTemplateSource(string name)
        {
            StringTemplateSource source;
            _templates.TryGetValue(name, out source);
            return source;
        }

        public long GetLastModified(object templateSource)
        {
            return ((StringTemplateSource) templateSource).LastModified;
        }

        public TextReader GetReader(object templateSource, Encoding encoding)
        {
            return new StringReader(((StringTemplateSource) templateSource).Source);
        }

        private class StringTemplateSource
        {
            private readonly string _name;
            internal readonly string Source;
            internal readonly long LastModified;

            internal StringTemplateSource(string name, string source, long lastModified)
            {
                if (name == null)
                {
                    throw new ArgumentNullException("name");
                }
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }
                if (lastModified < -1L)
                {
                    throw new ArgumentException("lastModified < -1L");
                }
                _name = name;
                Source = source;
                LastModified = lastModified;
            }

            public override bool Equals(object obj)
            {
                if (obj is StringTemplateSource)
                {
                    return _name == ((StringTemplateSource) obj)._name;
                }
                return false;
            }

            public override int GetHashCode()
            {
                return _name.GetHashCode();
            }
        }
    }
}